#include <stdio.h>

void send(unsigned char * to, unsigned char * from, unsigned short count)
{
    unsigned short n = (count + 7) / 8;

    switch (count % 8) {
        case 0: 
            do { *to = *from++;
        case 7:      *to = *from++;
        case 6:      *to = *from++;
        case 5:      *to = *from++;
        case 4:      *to = *from++;
        case 3:      *to = *from++;
        case 2:      *to = *from++;        
        case 1:      *to = *from++;
            } while (--n > 0);
    }
}

void send_low(unsigned char * to, unsigned char * from, unsigned short count)
{
    do {
        *to = *from++;
    } while (--count > 0);
}

void loop_01()
{
    long i = 0, k = 0;

    for (i = 0; i < 10000000000; i++) {
        k++;
    }
}

/*
 * 循环展开
 */
void loop_02()
{
    long i = 0, k = 0;

    for (i = 0; i < 10000000000; i += 8) {
        k++;k++;k++;k++;
        k++;k++;k++;k++;
    }
}

int main()
{
    loop_01();
    loop_02();

    return 0;
}



#if 0

/* send_low */
写法虽然简单，但太过低效。

性能低下,主要有两个问题：

• "无用"指令太多
• 无法充分发挥CPU的 ILP (Instruction-Level Parallelism, 指令级并行) 技术

所谓无用指令，是指不直接对所期望的结果产生影响的指令。

    只有 *to = *from++ 代码有用, 
    而每次迭代过程中的while (--count > 0)产生的指令，以及每次迭代结束后的跳转指令，对结果来说都是无用指令。

    反汇编指令热点路径中，无用指令数占了整个热点路径指令数的一半，其开销也占到整个函数的50%！


无法充分发挥ILP技术优势

    现代CPU为了提高指令执行的速度和吞吐率，提升系统性能，不仅一直致力于提升CPU的主频，
    还实现了多种ILP(Instruction-Level Parallelism 指令级并行)技术，如超流水线、超标量、乱序执行、推测执行、分支预测等。

    一个设计合理的程序，往往能够充分利用CPU的这些ILP机制，以使性能达到最优。
    但是，在代码热点路径上，无用指令太多，且每个迭代只执行一条*to = *from++，无法充分发挥ILP的技术优势。


所谓循环展开，是通过增加每次迭代内数据操作的次数，来减小迭代次数，甚至彻底消除循环迭代的一种优化手段。

    循环展开，有以下优点：

    • 有效减少循环控制指令。这些指令，是对结果不产生影响的无用指令。
      减少这些指令，就可以减少这些指令本身执行所需的开销，从而提升整体性能。

    • 通过合理的展开，可以更加有效地利用指令级并行ILP(Instruction-Level Parallelism 指令级并行)技术。

    循环展开是一个很常用的性能优化手段，所有现代编译器，通过合适的选项，都支持循环展开优化。

/*
 * 第一次优化尝试
 */
    先尝试把每次循环内拷贝字节的个数，由1个提高到到8个，这样就可以把迭代次数降低8倍。
    先假设，send()函数的参数count总是8的倍数，那么上面的代码就可以修改为：

    void send(unsigned char * to, unsigned char * from, unsigned short count)
    {
        unsigned short n = (count) / 8;

        do { 
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;        
            *to = *from++;
        } while (--n > 0);
    }

    把原来迭代里的操作复制了8次，然后把迭代次数降低到了8倍。

    但是，前面做了一个假设，就是count是8的倍数。那如果不是8的整数倍呢，比如20？

    void send(unsigned char * to, unsigned char * from, unsigned short count)
    {
        unsigned short n = (count) / 8;

        do { 
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;
            *to = *from++;        
            *to = *from++;
        } while (--n > 0);

        n = count % 8;
        while (n-- > 0) {
            *to = *from++;
        }
    }

    相比原始的实现来说，性能已经能提升了不少了。
    但是第二个while循环非常不爽，尽管对整体性能已经没有太大影响了。
    于是有了下面的代码；

    C语言中switch-case语句的一些特性：
    • case语句后面的break语句不是必须的。
    • 在switch语句内，case标号可以出现在任意的子语句之前，甚至运行出现在if、for、while等语句内。


    假设count = 20
        n = (count + 7) / 8 = 27 / 8 = 3
        count % 8 = 4
    
    1. switch 语句会落入 case 4 的标签内，然后依次执行了 case 4、3、2、1 四条语句。
       自此之后，其实就跟 switch-case 语句再也没有关系了。

    2. while 语句判断 --n > 0，条件成立，于是跳转到 case 0 进入循环体执行，
       于是依次执行 case 0、7、6、5、4、3、2、1 一共8条语句。此时 n = 2.

    3. 再次进入 while 语句处判断 --n > 0，条件成立，再次跳转到case 0处进入循环体执行。此时 n = 1。

    4. 此时， while 语句处判断 --n > 0，条件失败，退出循环，函数结束。

#endif
